%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%  Copyright by Wenliang Du.                                       %%
%%  This work is licensed under the Creative Commons                %%
%%  Attribution-NonCommercial-ShareAlike 4.0 International License. %%
%%  To view a copy of this license, visit                           %%
%%  http://creativecommons.org/licenses/by-nc-sa/4.0/.              %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\newcommand{\commonfolder}{../../common-files}

\input{../../common-files/header}
\input{../../common-files/copyright}

\newcommand{\dnsFigs}{./Figs}
\lhead{\bfseries SEED Labs -- 远程DNS缓存中毒攻击实验}


\def \code#1 {\fbox{\scriptsize{\texttt{#1}}}}

\begin{document}
\begin{center}
{\LARGE 远程 DNS 缓存中毒攻击（Kaminsky 攻击）实验}
\end{center}

\seedlabcopyright{2006 - 2020}


% *******************************************
% SECTION
% ******************************************* 
\section{实验综述}


本实验的目的是让学生掌握远程 DNS 缓存中毒攻击 (也被称为 Kaminsky DNS 攻击) 的经验，
DNS (Domain Name System) 是互联网的电话本，它可以帮助用户通过主机名找到 IP 地址，或者反向查询。
这种查询的过程是比较复杂的，DNS攻击便是针对于这一过程，
以各种方式将用户误导至攻击者提供的 IP 地址 (该IP往往是恶意的)。
本实验关注于一种叫做 DNS 缓存中毒攻击的攻击技术。
在另一个 SEED 实验中，我们设计了一个在本地网络环境下的DNS缓存中毒攻击实验，
即攻击者和受害的 DNS 服务器位于同一网络中。在这种情况下，攻击者可以使用数据包嗅探的技术。
而在远程攻击中，没法用数据包嗅探，因此远程DNS攻击变得更有挑战和难度。
本实验涵盖以下主题：


\begin{itemize}[noitemsep]
\item DNS介绍与DNS工作原理
\item DNS服务器搭建
\item DNS缓存中毒攻击
\item 伪造DNS响应
\item 数据包伪造
\end{itemize}


\paragraph{相关阅读材料与视频：}
关于DNS协议与攻击的详细内容可以参考以下材料:


\begin{itemize}
  \item SEED 书的第 18 章，\seedbook
  \item SEED 视频的第 7 节，\seedisvideo
\end{itemize}


\paragraph{实验环境} \seedenvironmentC


% \vspace{0.2in}
% \noindent
% \fbox{\parbox{\textwidth}{
% \noindent
% \textbf{用户定制：}
% 在此次实验描述中，我们使用\texttt{attacker32.com}域作为攻击者控制的恶意域名。
% 当学生在进行本实验时，不被允许使用这个域名，学生应当使用一个包含自己名字的域名。
% 该要求的目的是区分学生的工作，由于该域名只在实验环境中可见，并不公开使用，因此任何域名
% (即使已被他人拥有的域名)都可以在实验环境中安全地使用。
% }}




% *******************************************
% SECTION
% ******************************************* 
\section{实验环境搭建(任务1)}
\label{sec:environment}

\begin{figure}[htb]
\centering
\includegraphics[width=0.9\textwidth]{\dnsFigs/environment_setup_remote.pdf}
\caption{实验环境搭建}
\label{dns:fig:environment}
\end{figure}


DNS缓存中毒攻击的主要目标是本地 DNS 服务器。显然，攻击真实的 DNS 服务器是违法的，因此我们需要搭建自己的 DNS 服务器来进行攻击实验。实验环境需要四台独立的机器：一台用于模拟受害者，一台用作 DNS 服务器，两台用于攻击者。图~\ref{dns:fig:environment}描述了实验环境的设置。

为了简单起见，我们将这些 VM 都放在同一 LAN (局域网)中，但学生不可以在攻击中利用这一点，他们
应该将攻击者的机器视为远程机器，即攻击者无法在 LAN 上嗅探数据包。这与本地DNS攻击有所不同。



% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{容器配置和命令}
  

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\commonfolder/container/setup}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{攻击者容器}

在本实验中，我们既可以使用虚拟机（VM），也可以使用攻击者容器作为攻
击机器。如果你查看 Docker Compose文件，就会发现攻击者容器的配置与其他容器有所不同。

\begin{itemize}
\item \textit{Shared folder.} 当我们使用攻击者容器发起攻击时，需要将攻击代码置于攻击者容器内。 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\commonfolder/container/volumes}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

  
\item \textit{Host mode.}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\commonfolder/container/host_mode}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\end{itemize}


% -------------------------------------------
% SUBSECTION
% -------------------------------------------
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\commonfolder/DNS/summary_of_config}
\input{\commonfolder/DNS/setup_testing}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% *******************************************
% SECTION
% ******************************************* 
\section{攻击任务}

DNS攻击的主要目的是在用户使用$A$的域名前往$A$主机时，将用户导向到另一个主机$B$。
例如，假设 www.example.com 是一个在线银行网站，当用户尝试使用正确的 URL www.example.com
访问该网站时，如果攻击者可以将用户重定向到一个非常类似于 www.example.com 的恶意站点，
那么用户很有可能被欺骗并向攻击者泄露自己的用户名和密码信息。


在此任务中，我们将域名 www.example.com 作为我们的攻击目标。需要注意的是，example.com 
是一个保留域名，并不用作任何真实用途，所以我们可以放心使用。 www.example.com 的真实IP地址为 93.184.216.34， 它的域名服务器由 Internet Corporation for Assigned Names and Numbers (ICANN) 管理。当用户
用 dig 命令查询 www.example.com 的 IP 地址时，或在浏览器中输入该域名时，用户主机会向本地 DNS 服务器发送 DNS 查询请求，该 DNS 服务器最终将从 example.com 的域名服务器中得到 IP 地址。


攻击的目标是对本地 DNS 服务器进行 DNS 缓存投毒攻击，使得当用户运行 dig 命令来查找 www.example.com 的 IP 地址时，本地 DNS 服务器会从攻击者的域名服务器 ns.attacker32.com 获得 IP 地址，因此返回的 IP 地址是攻击者提供的。导致的结果是用户会被导向攻击者的恶意站点，而不是真实的 www.example.com。




\begin{figure}[htb]
\centering
\includegraphics[width=0.9\textwidth]{\dnsFigs/DNS_Remote_new1.pdf}
\caption{完整的DNS查询过程} 
\label{fig:flow_diagram1}
\end{figure}


\begin{figure}[htb]
\centering
\includegraphics[width=0.9\textwidth]{\dnsFigs/DNS_Remote_new2.pdf}
\caption{Kaminsky攻击}
\label{fig:flow_diagram2}
\end{figure}



% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{Kaminsky攻击原理}

在此任务中，攻击者向受害 DNS 服务器 ({\tt Apollo}) 发送 DNS 查询请求，从而触发来自
{\tt Apollo} 的 DNS 查询。DNS 查询首先前往其中一个根 DNS 服务器，接着是 {\tt .COM} 的 DNS服务器，最终
从 {\tt example.com} 的 DNS 服务器得到查询结果，查询过程如图~\ref{fig:flow_diagram1} 所示。
如果 {\tt example.com} 的域名服务器信息已经被 {\tt Apollo} 缓存，
那么查询不会前往根服务器或 {\tt .COM} DNS 服务器，这个过程如图~\ref{fig:flow_diagram2} 所示。
在本实验中，图~\ref{fig:flow_diagram2} 描绘的场景更为常见，因此我们以这个图为基础来描述攻击机制。


当{\tt Apollo}等待来自{\tt example.com}域名服务器的DNS答复时，攻击者可以发送伪造的答复给{\tt Apollo}，
假装这个答复是来自 {\tt example.com} 的域名服务器。如果伪造的答复先到达而且有效，那么它将被
{\tt Apollo} 接收，攻击成功。


如果你已经做了本地 DNS 攻击的实验，你应该会知道那个实验的攻击是假定攻击者和DNS服务器位于同一局域网，
所以攻击者可以捕捉到DNS查询数据包。但当攻击者与DNS服务器不在同一局域网时，缓存投毒攻击会变得非常困难。
主要的难点在于DNS响应中的 Transcation ID 必须与查询请求中的相匹配。由于查询中的 ID
通常是随机生成的，在看不到请求包的情况下，攻击者很难猜到正确的ID。


当然，攻击者可以猜测 Transcation ID。由于这个 ID 只有16 个比特大小，如果攻击者可以在攻击窗口内伪造
$K$ 个响应 (即在合法响应到达之前)，那么攻击成功的可能性就是 $K$/$2^{16}$。发送数百个伪造响应
并不是不切实际的，因此攻击成功是比较容易的。

然而，上述假设的攻击忽略了 DNS 缓存。在现实中，如果攻击者没有在合法的响应到达之前猜中正确的 Transcation ID，那么DNS服务器会将正确的信息缓存一段时间。这种缓存效果使攻击者无法继续伪造针对该域名的响应，
因为 DNS 服务器在缓存超时之前不会针对该域名发出另一个DNS查询请求。为了继续对同一个域名的
响应做伪造，攻击者必须等待针对该域名的另一个DNS查询请求，这意味着他必须要等到缓存超时，而等待
时间会长达几小时甚至是几天。


\paragraph{Kaminsky攻击。} 
Dan Kaminsky 提出了一个巧妙的方法来解决缓存的问题~\cite{dns:Kaminsky}。
通过他的方案，攻击者可以持续地发起欺骗攻击，而不需要等待，因此攻击可以在很短的
一段时间内成功。攻击的详细描述在~\cite{dns:Kaminsky,seedbook} 可以找到。
在本任务中，我们将尝试这个攻击手段。以下步骤（基于图~\ref{fig:flow_diagram2}）概述了攻击的过程。


\begin{enumerate}
\item 攻击者向DNS服务器{\tt Apollo} 查询{\tt example.com}域中一个不存在的主机名，
如{\tt twysw.example.com}，其中{\tt twysw}是一个随机的名字。 

\item 由于{\tt Apollo}的DNS缓存中不会有这个主机名，{\tt Apollo} 会向{\tt example.com}域的域名服务器发送一个DNS查询请求。

\item 当{\tt Apollo} 等待答复时，攻击者向 {\tt Apollo}发送大量的伪造的DNS响应，每个响应尝试一个不同的 Transaction ID(期望其中一个是正确的)。在响应中，攻击者不仅提供{\tt twysw.example.com}的
IP 地址，还提供了一条``权威授权服务器''记录，指明{\tt ns.attacker32.com}是
{\tt example.com}域的域名服务器。如果伪造的响应比实际响应到达的早，且 Transaction ID 与请求中的 ID 一样，{\tt Apollo}就会接受并缓存伪造的答案。这样{\tt Apollo}的DNS缓存就被投毒成功了。

\item 即使伪造的DNS响应失败了(例如，Transaction ID 不匹配或到达的太晚了)，也没有关系，因为下一次攻击者会查询另一个主机名，所以 {\tt Apollo} 会发送另一个DNS请求，从而给攻击者提供了另一个伪造的机会。这种方法有效地克服了DNS缓存效果。

\item 如果攻击成功，那么在{\tt Apollo}的DNS缓存中，{\tt example.com}域的域名服务器会被攻击者替换成{\tt ns.attacker32.com}。为证明成功攻击，学生需要展示在{\tt Apollo}的DNS缓存中存在这样一条记录。
 
\end{enumerate}


\paragraph{任务综述。} 实现Kaminsky攻击具有很强的挑战性，因此我们将它分解为好几个子任务。在
任务2中，我们构造一个\texttt{example.com}域内主机名的DNS查询请求。在任务3中，我们构
造一个从 \texttt{example.com}域名服务器返回的伪造响应。在任务4中，我们前面的工作整合到一起，进行Kaminsky攻击。最后我们在任务5中验证攻击的效果。


% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务 2: 构造DNS请求} 

任务2专注于发送DNS请求。为了完成攻击，攻击者需要触发目标DNS服务器发出DNS查询，这样攻击者
才有机会去伪造DNS响应。由于攻击者需要尝试多次才能成功，因此最好使用程序来自动发送DNS查询。

学生需要编写一个程序来向目标服务器发送DNS请求(即我们配置的本地DNS服务器)。具体任务是
编写该程序并证明(使用Wireshark)他们的查询请求可以触发目标DNS服务器会发出相应的DNS查询。
该任务对性能的要求不高，因此学生可以使用C语言或Python(使用Scapy)编写此代码。以下提供了
Python的代码示例(学生需要将其中的\texttt{+++}替换为实际的值)。


\begin{lstlisting}
Qdsec  = DNSQR(qname='www.example.com')
dns    = DNS(id=0xAAAA, qr=0, qdcount=1, ancount=0, nscount=0,
             arcount=0, qd=Qdsec)

ip  = IP(dst='+++', src='+++')
udp = UDP(dport=+++, sport=+++, chksum=0)
request = ip/udp/dns
\end{lstlisting}
 

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务 3: 伪造DNS响应}   

在此任务中，我们需要伪造Kaminsky攻击中的DNS响应。由于我们的攻击目标是\texttt{example.com}，
我们需要伪造从该域的域名服务器返回的响应。学生首先需要找到\texttt{example.com}的合法域名服务器
的IP地址(值得注意的是这个域名有多个域名服务器)。

学生可以使用Scapy来实现这个任务，以下的代码示例构建了一个DNS响应包，其中包含了问题部分，
回答部分以及一个域名服务器部分。在这段代码中，我们使用\texttt{+++}作为占位符，学生
需要用Kaminsky攻击中所需要的值来替换。学生需要解释为什么选择这些值。

\begin{lstlisting}
name   = '+++'  
domain = '+++'  
ns     = '+++'

Qdsec  = DNSQR(qname=name)
Anssec = DNSRR(rrname=name,   type='A',  rdata='1.2.3.4', ttl=259200)
NSsec  = DNSRR(rrname=domain, type='NS', rdata=ns, ttl=259200)
dns    = DNS(id=0xAAAA, aa=1, rd=1, qr=1,
             qdcount=1, ancount=1, nscount=1, arcount=0,
             qd=Qdsec, an=Anssec, ns=NSsec)

ip    = IP(dst='+++', src='+++')
udp   = UDP(dport=+++, sport=+++, chksum=0)
reply = ip/udp/dns
\end{lstlisting}
 

由于这些响应本身并不能成功实施攻击，为了展示任务完成的效果，学生需要使用Wireshark来
捕获伪造的DNS响应，并证明这些伪造的数据包是合法的。



% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务 4: 进行Kaminsky攻击}   

现在我们将所有东西合在一起进行Kaminsky攻击。在攻击中，我们需要发送许多欺骗的DNS响应，
希望其中有一个可以猜中正确的 Transaction ID，并比合法的响应更早到达。因此，发包速度至关重要：发出
的数据包越多，成功的概率也就越大。如果我们像之前的任务那样，使用 Scapy 发送伪造的 DNS 
回复，成功率会非常低。学生可以使用C语言进行实现，但在C语言中构造DNS数据包并非易事。
因此我们采用一个使用C语言和Scapy相结合的混合方法。(详情可参考SEED教材)

通过混合方法，我们首先使用Scapy生成DNS数据包模板，并把模板保存在文件中。
接着我们将该数据包模板加载到C程序中，并对其中某些字段进行一些微小修改，然后发出这个数据包。
我们在\path{Labsetup/Files/attack.c}中提供了C语言的代码框架。
学生可以对其中标记的区域进行修改，详细的代码解释在之后的指南部分中。


\paragraph{检查DNS缓存：}
为了检查攻击是否成功，我们需要查看DNS缓存。
以下的命令先将 DNS 缓存存到文件中，然后在文件中搜寻 \texttt{attacker} 关键词 (在我们的攻击中，我们
采用\texttt{attacker32.com} 作为攻击者的域名，如果学生使用不同的攻击域名，那么需要搜索不同的关键词)。
 
\begin{lstlisting}
  # rndc dumpdb -cache && grep attacker /var/cache/bind/dump.db
\end{lstlisting}
 

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务 5: 攻击结果验证}

如果攻击成功，在本地DNS服务器的缓存中，\texttt{example.com}的{\tt NS}记录应该会改为
\texttt{ns.attacker32.com}。当服务器收到对\texttt{example.com}域内的任何域名的查询请求时，
它会向\texttt{ns.attacker32.com}发送查询请求，而不是向原本合法的域名服务器。


为了验证攻击是否成功，在用户主机上运行以下两条\texttt{dig}命令。在两次响应中，
\texttt{www.example.com}的IP地址应该相同，并且应该是在攻击主机的区域文件中配置的内容。


\begin{lstlisting}
//向本地 DNS 服务器发出查询请求
$ dig www.example.com

//直接向 attacker32 域名服务器发出请求
$ dig @ns.attacker32.com www.example.com
\end{lstlisting}
 
请在实验报告中给出你的观察结果(截图)，并解释你认为攻击成功的原因。
需要注意的是，当你第一次运行\texttt{dig}命令时，请使用Wireshark来捕获网络流量，
并指出\texttt{dig}命令触发了哪些数据包。根据数据包追踪来证明你的攻击是成功的。
请注意，在运行第一个\texttt{dig}命令后，DNS 结果可能会缓存在本地 DNS 服务器上。如果你
在使用 Wireshark 之前运行了第一个\texttt{dig}命令，这可能会影响结果。你可以在本地 DNS 
服务器上使用 \texttt{"sudo rndc flush"} 清除缓存，但这将需要你重新进行攻击。



% *******************************************
% SECTION
% ******************************************* 
\section{指南} 

为了实现Kaminsky攻击，我们使用Scapy进行数据包伪造。但Python每秒
生成的数据包太少以至于攻击很难成功。所以最好的方法是使用C语言程序，然而这对于许多学生来说颇具挑战，
因为用C语言构造DNS数据包并非易事。我们开发了一种混合方法，并在课堂上进行了实验。通过这种方法，
可以大大减少学生在编程上花费的时间，因此他们可以将更多的时间用于关注攻击本身。


这个方法是同时利用Scapy和C的优势：Scapy在构建DNS数据包方面远远的比C更方便，但是C速度更快。
因此我们使用Scapy构建伪造的DNS数据包，并将它保存到文件中，接着我们将数据包加载到C程序中。
尽管在Kaminsky攻击过程中，我们需要发送许多不同的DNS数据包，但除了少数字段外，这些数据包几乎相同。
我们可以将Scapy生成的数据包作为基础，找到需要修改地方的偏移量(如 Transaction ID 字段)，直接进行修改。
这比在C中创建整个DNS数据包要简单很多。进行修改之后，我们使用原始的套接字发送这些数据包。有关这种混合方法的详细信息，请参见SEED书~\cite{seedbook}中《数据包嗅探和伪造》一章。以下的Scapy程序会创建一个简单的DNS响应数据包并将其保存在文件中。


\begin{lstlisting}[caption={\texttt{generate\_dns\_reply.py}}]
#!/usr/bin/env python3
from scapy.all import *
  
# Construct the DNS header and payload
name   = 'twysw.example.com'
Qdsec  = DNSQR(qname=name)
Anssec = DNSRR(rrname=name, type='A', rdata='1.1.2.2', ttl=259200)
dns    = DNS(id=0xAAAA, aa=1, rd=0, qr=1, 
               qdcount=1, ancount=1, nscount=0, arcount=0, 
               qd=Qdsec, an=Anssec)
  
# Construct the IP, UDP headers, and the entire packet
ip  = IP(dst='10.0.2.7', src='1.2.3.4', chksum=0)
udp = UDP(dport=33333, sport=53, chksum=0)
pkt = ip/udp/dns
  
# Save the packet to a file
with open('ip.bin', 'wb') as f:
  f.write(bytes(pkt))
\end{lstlisting}

在C程序中，我们从文件\texttt{ip.bin}中读入数据包，并将其用作数据包的模板，在此模板上，
我们可以创建许多类似的数据包，并向本地DNS服务器发送这些伪造的相应数据包。对于每个响应，我们修改
三个地方：Transaction ID 和在两个位置(查询字段和应答字段)出现的\texttt{twysw}。Transaction ID在一个固定的位置(从IP数据包开头偏移量\texttt{28})，但名称\texttt{twysw}的偏移位置取决于域名的长度。
我们可以使用二进制编辑器，如\texttt{ghex}，来查看二进制文件\texttt{ip.bin}并找到
\texttt{twysw}的两个偏移量。在我们的数据包中，他们的偏移量是\texttt{41} 和 \texttt{64}。


以下的代码片段显示了我们如何修改这些字段。我们将响应中的域名改为\texttt{bbbbb.example.com}，
并发出一个伪造的DNS答复(Transaction ID 为\texttt{1000})。在代码中，变量\texttt{ip}指向IP数据包的起始点。
 

\begin{lstlisting}
  // 修改查询字段中的域名 (offset=41)
  memcpy(ip+41, "bbbbb" , 5);

  // 修改答案字段中的域名 (offset=64)
  memcpy(ip+64, "bbbbb" , 5);

  // 修改 Transaction ID 字段 (offset=28)
  unsigned short id = 1000;
  unsigned short id_net_order = htons(id);
  memcpy(ip+28, &id_net_order, 2);
\end{lstlisting}



\paragraph{生成随机子域名：} 在 Kaminsky 攻击中，我们需要生成随机的子域名。
有许多方法可以做到这一点。以下的代码片段展示了如何生成一个5个字符的随机子域名。

\begin{lstlisting}
char a[26]="abcdefghijklmnopqrstuvwxyz";
  
// 生成长度为5的随机名称
char name[6];
name[5] = 0;
for (int k=0; k<5; k++)  
   name[k] = a[rand() % 26];
\end{lstlisting}
   
  
\paragraph{编译程序}
我们可以运行以下命令来编译程序：
  
\begin{lstlisting}
$ gcc -o attack attack.c
  
// Apple 机型请使用静态绑定：
$ gcc -static -o attack attack.c
\end{lstlisting}
 



% *******************************************
% SECTION
% ******************************************* 
\section{Submission}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\commonfolder/submission}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\thispagestyle{empty}
\bibliographystyle{plain}
\def\baselinestretch{1}
\bibliography{BibDNS}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



\end{document}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

